
#include "gkt_board.h"
#include "gkt_driver.h"
#include "gkt_debug.h"
#include "gkt_timer.h"
#include "gkt_gpio.h"

#define TIMER_CHANNEL_MAXNUMS	9

/************************************************************
 * timer pwm padmux
 *   if PWM function of TIMER-x is used, defines follow macros:
 *     #define GKT_CONFIG_TIMER0_PWM_PAD	GKT_GPIO_PAD(GPIOX, PWM_OUTX)
 ************************************************************/
static const gkt_gpio_padmux_desc_s sc_timer_pwm_padmux_descs[TIMER_CHANNEL_MAXNUMS] = {
#if defined(GKT_CONFIG_TIMER0_PWM_PAD) && defined(GKT_CONFIG_TIMER0_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER0_PWM_PAD, GKT_CONFIG_TIMER0_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER1_PWM_PAD) && defined(GKT_CONFIG_TIMER1_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER1_PWM_PAD, GKT_CONFIG_TIMER1_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER2_PWM_PAD) && defined(GKT_CONFIG_TIMER2_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER2_PWM_PAD, GKT_CONFIG_TIMER2_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER3_PWM_PAD) && defined(GKT_CONFIG_TIMER3_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER3_PWM_PAD, GKT_CONFIG_TIMER3_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER4_PWM_PAD) && defined(GKT_CONFIG_TIMER4_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER4_PWM_PAD, GKT_CONFIG_TIMER4_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER5_PWM_PAD) && defined(GKT_CONFIG_TIMER5_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER5_PWM_PAD, GKT_CONFIG_TIMER5_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER6_PWM_PAD) && defined(GKT_CONFIG_TIMER6_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER6_PWM_PAD, GKT_CONFIG_TIMER6_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER7_PWM_PAD) && defined(GKT_CONFIG_TIMER7_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER7_PWM_PAD, GKT_CONFIG_TIMER7_PWM_FUNC),
#else
	{ 0, 0 },
#endif

#if defined(GKT_CONFIG_TIMER8_PWM_PAD) && defined(GKT_CONFIG_TIMER8_PWM_FUNC)
	GKT_GPIO_PADMUX_DESC_DEFINE(GKT_CONFIG_TIMER8_PWM_PAD, GKT_CONFIG_TIMER8_PWM_FUNC),
#else
	{ 0, 0 },
#endif
};

typedef struct _timer {
	uint32_t	channel_opened_bits;
	uint32_t	channel_configed_bits;
	uint32_t	channel_start_bits;
	gkt_timer_callback	callbacks[TIMER_CHANNEL_MAXNUMS];	// if NULL, PWM mode
} timer_s;
static timer_s s_timer;

#define TIMER_MAGIC	0x54494D00
#define TIMER_MAGIC_MASK	0xffffff00
#define TIMER_CHANNEL_HANDLE(id)	(TIMER_MAGIC | (id))
#define TIMER_CHANNEL_ID(handle)	((handle) & 0xff)

#define TIMER_HANDLE_IS_VALID(handle)	\
	((TIMER_MAGIC == ((handle) & TIMER_MAGIC_MASK)) \
		&& (TIMER_CHANNEL_ID(handle) < TIMER_CHANNEL_MAXNUMS))

/*
 * TMRG0: Channel 0~3
 * TMRG1: Channel 4~8
 */
void gkt_timer_irq_handler(void)
{
	uint32_t channel_id;
	uint32_t status;

	status = MTIM->IRQ_NUM.reg;
	for (channel_id = 0; channel_id < TIMER_CHANNEL_MAXNUMS; channel_id++) {
		if (status & (1U << channel_id)) {
			G_TIM_ClearITPendingBit(MTIM, (G_TIM_NumTypeDef)channel_id);

			if (s_timer.channel_start_bits & (1U << channel_id)) {
				if (s_timer.callbacks[channel_id])
					(*s_timer.callbacks[channel_id])(TIMER_CHANNEL_HANDLE(channel_id));
			}
			else
				G_TIM_ITConfig(MTIM, (G_TIM_NumTypeDef)channel_id, DISABLE);
		}
	}

}

gkt_handle_t gkt_timer_open(uint32_t type, 
					uint32_t dev_id, uint32_t channel_id)
{
	gkt_handle_t handle = GKT_HANDLE_INVALID;

	if ((GKT_TIMER_TYPE_BASE == type) 
		&& (channel_id < TIMER_CHANNEL_MAXNUMS)) 
	{
		s_timer.channel_opened_bits |= 1U << channel_id;
		handle = TIMER_CHANNEL_HANDLE(channel_id);
	}

	return handle;
}

int gkt_timer_set_pwm_period(gkt_handle_t handle, uint32_t pwm_cnt)
{
	uint32_t channel_id,lowlevelperiod,highlevelperiod;
	if (TIMER_HANDLE_IS_VALID(handle)) {
		channel_id = TIMER_CHANNEL_ID(handle);
		lowlevelperiod =
			(uint32_t)(100 - pwm_cnt) * ((GKT_SYSTEM_CLOCK_HZ / 192) / 100);	// TODO:
		highlevelperiod = 
			(uint32_t)pwm_cnt * ((GKT_SYSTEM_CLOCK_HZ / 192) / 100);	// TODO:
		G_TIM_SetPWMPeriod(MTIM, (G_TIM_NumTypeDef)channel_id, lowlevelperiod, highlevelperiod);
	}
	return GKT_SUCCESS;
}

int gkt_timer_config(gkt_handle_t handle, void *attr)
{
	const gkt_base_timer_cfg_attr_s *c_cfg_attr;
	uint32_t channel_id, mask;

	if (TIMER_HANDLE_IS_VALID(handle)
		&& attr) 
	{
		channel_id = TIMER_CHANNEL_ID(handle);
		mask = 1U << channel_id;
		if (!(s_timer.channel_opened_bits & mask))
			return GKT_ENOTOPEN;
		else if (s_timer.channel_start_bits & mask)
			return GKT_EPERMIT;

		c_cfg_attr = (const gkt_base_timer_cfg_attr_s *)attr;
		if (c_cfg_attr->mode < GKT_TIMER_MODE_MAX) {
			if (GKT_TIMER_MODE_TIMER == c_cfg_attr->mode) {
				G_TIM_InitTypeDef TIM_Init_Struct;

				if (!c_cfg_attr->timer.interval 
					|| !c_cfg_attr->timer.callback)	/* no callback */
				{
					return GKT_EPARAM;
				}
				s_timer.callbacks[channel_id] = c_cfg_attr->timer.callback;

				TIM_Init_Struct.TIMx = (G_TIM_NumTypeDef)channel_id;
				TIM_Init_Struct.Auto_Reload = c_cfg_attr->timer.auto_reload ? ENABLE : DISABLE;
				// TODO: get period from c_cfg_attr->timer.interval & TIMER clock//G_SYSCTRL_GetClocksFreq(MCU_FRE)
				TIM_Init_Struct.period = 
				(uint32_t)(((uint64_t)c_cfg_attr->timer.interval * (GKT_SYSTEM_CLOCK_HZ / 4)) / 1000000) - 1;	// TODO:
				G_TIM_Init(MTIM, &TIM_Init_Struct);
				G_TIM_ModeConfig(MTIM, TIM_Init_Struct.TIMx, TIM_Mode_TIMER);
			}
			else {	/* (GKT_TIMER_MODE_PWM == c_cfg_attr->mode) */
				const gkt_gpio_padmux_desc_s *c_padmux_desc;
				G_PWM_InitTypeDef TIM_PWM_Init_Struct;

				c_padmux_desc = &sc_timer_pwm_padmux_descs[channel_id];
				if (!c_padmux_desc->func)
					return GKT_ENOTSUPPORT;

				s_timer.callbacks[channel_id] = NULL;
				gkt_gpio_padmux_config(c_padmux_desc);

				TIM_PWM_Init_Struct.TIMx = (G_TIM_NumTypeDef)channel_id;
				// TODO: get xxx_period from c_cfg_attr->pwm.freq, duty_cycle and TIMER clock
				TIM_PWM_Init_Struct.LowLevelPeriod = //LowLevelPeriod;//10000000
					(uint32_t)(100 - c_cfg_attr->pwm.duty_cycle) * ((GKT_SYSTEM_CLOCK_HZ / 192) / c_cfg_attr->pwm.frequence);	// TODO:
				TIM_PWM_Init_Struct.HighLevelPeriod = //;40000000 HighLevelPeriod
					(uint32_t)(c_cfg_attr->pwm.duty_cycle) * ((GKT_SYSTEM_CLOCK_HZ / 192) / c_cfg_attr->pwm.frequence);	// TODO:
				//gkt_printf("low=%d hig=%d\n", TIM_PWM_Init_Struct.LowLevelPeriod, TIM_PWM_Init_Struct.HighLevelPeriod);
				TIM_PWM_Init_Struct.StartLevel = c_cfg_attr->pwm.start_level ? HIGH_LEVEL : LOW_LEVEL;
				G_TIM_PWMInit(MTIM, &TIM_PWM_Init_Struct);
				G_TIM_ModeConfig(MTIM, TIM_PWM_Init_Struct.TIMx, TIM_Mode_PWM);
			}
		}
		else 
			return GKT_EPARAM;
		
		s_timer.channel_configed_bits |= mask;

		return GKT_SUCCESS;
	}

	return GKT_EPARAM;
}

int gkt_timer_start(gkt_handle_t handle)
{
	uint32_t channel_id, mask;

	if (TIMER_HANDLE_IS_VALID(handle)) {
		channel_id = TIMER_CHANNEL_ID(handle);
		mask = 1U << channel_id;
		if (!(s_timer.channel_configed_bits & mask))
			return GKT_ENOTCONFIG;

		if (!(s_timer.channel_start_bits & mask)) {
			if (s_timer.callbacks[channel_id]) 
				G_TIM_ITConfig(MTIM, (G_TIM_NumTypeDef)channel_id, ENABLE);
			G_TIM_Cmd(MTIM, (G_TIM_NumTypeDef)channel_id, ENABLE);

			s_timer.channel_start_bits |= mask;
		}

		return GKT_SUCCESS;
	}

	return GKT_EPARAM;
}

static void timer_stop(uint32_t channel_id)
{
	uint32_t mask;

	mask = 1U << channel_id;
	if (s_timer.channel_start_bits & mask) {
		s_timer.channel_start_bits &= ~mask;
		G_TIM_ITConfig(MTIM, (G_TIM_NumTypeDef)channel_id, DISABLE);
		G_TIM_Cmd(MTIM, (G_TIM_NumTypeDef)channel_id, DISABLE);
	}
}

int gkt_timer_stop(gkt_handle_t handle)
{
	if (TIMER_HANDLE_IS_VALID(handle)) {
		timer_stop(TIMER_CHANNEL_ID(handle));
		return GKT_SUCCESS;
	}

	return GKT_EPARAM;
}

void gkt_timer_close(gkt_handle_t handle)
{
	uint32_t channel_id, mask;

	if (TIMER_HANDLE_IS_VALID(handle)) {
		channel_id = TIMER_CHANNEL_ID(handle);
		mask = 1U << channel_id;
		if (s_timer.channel_start_bits & mask)
			timer_stop(channel_id);
		s_timer.channel_configed_bits &= ~mask;
		s_timer.channel_opened_bits &= ~mask;
	}
}

int gkt_timer_init(void)
{
	gkt_irq_set_priority(GKT_TIMER0_IRQ_NO, GKT_IRQ_PRIORITY_NORMAL);
	gkt_irq_enable(GKT_TIMER0_IRQ_NO);

	gkt_irq_set_priority(GKT_TIMER1_IRQ_NO, GKT_IRQ_PRIORITY_NORMAL);
	gkt_irq_enable(GKT_TIMER1_IRQ_NO);
	return GKT_SUCCESS;
}

GKT_DEVICE_DESC_DEFINE(timer, BASE);

